﻿/*
 * @author: wizardc
 */

using System.Collections.Generic;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Rendering;

namespace Dou.GPU
{
    /// <summary>
    /// 使用 GPUInstancing 绘制的蒙皮动画管理器
    /// 注意：当前管理器是基于 GPUInstancingMgr 实现底层绘制的，整个动画的控制都应该只访问 GPUSkinnedMeshMgr 而不要尝试去 GPUInstancingMgr 里修改调整动画的相关数据
    /// </summary>
    public unsafe partial class GPUSkinnedMeshMgr
    {
        private static GPUSkinnedMeshMgr _instance;
        public static GPUSkinnedMeshMgr instance => _instance ??= new GPUSkinnedMeshMgr();
        
        private static readonly int ID_FrameWidth = Shader.PropertyToID("frameWidth");
        private static readonly int ID_FrameHeight = Shader.PropertyToID("frameHeight");

        private int _batchId = 0;
        private Dictionary<int, GPUBatchAnimation> _batchMap = new Dictionary<int, GPUBatchAnimation>();
        private Dictionary<int, int> _uid2BatchId = new Dictionary<int, int>();
        
        private GPUSkinnedMeshMgr()
        {
        }

        /// <summary>
        /// 注册一个蒙皮动画
        /// </summary>
        /// <param name="animData">动画数据</param>
        /// <param name="maximum">当前批次最多可提交的数量，超过该数量的对象会被拆分为多次提交</param>
        /// <param name="castShadows">是否投射阴影</param>
        /// <param name="receiveShadows">是否接收阴影</param>
        /// <param name="layer">绘制到的层</param>
        /// <param name="camera">绘制到的摄像机</param>
        /// <returns>动画批次ID</returns>
        public int RegisterAnimation(Mesh mesh, Material material, GPUAnimData animData, int maximum = 256, ShadowCastingMode castShadows = ShadowCastingMode.Off, bool receiveShadows = false, int layer = 0, Camera camera = null)
        {
            var batchId = GPUInstancingMgr.instance.RegisterBatch(mesh, material, maximum, castShadows, receiveShadows, layer, camera);
            material.SetFloat(ID_FrameWidth, 1f / animData.frameCount);
            material.SetFloat(ID_FrameHeight, 1f / (animData.boneCount * 3));
            var batch = new GPUBatchAnimation(batchId, animData);
            _batchMap.Add(_batchId, batch);
            return _batchId++;
        }

        /// <summary>
        /// 注销一个蒙皮动画
        /// </summary>
        public void UnregisterAnimation(int animBatchId)
        {
            if (_batchMap.ContainsKey(animBatchId))
            {
                var batch = _batchMap[animBatchId];
                foreach (var uid in batch.EnumerateUid())
                {
                    _uid2BatchId.Remove(uid);
                }
                batch.Dispose();
                _batchMap.Remove(animBatchId);
            }
        }
        
        /// <summary>
        /// 创建一个动画对象
        /// </summary>
        /// <param name="animBatchId">动画批次ID</param>
        /// <returns>当前动画唯一ID</returns>
        public int CreateAnimation(int animBatchId, float3 position, quaternion rotation, float3 scale)
        {
            if (!_batchMap.ContainsKey(animBatchId))
            {
                return -1;
            }
            var batch = _batchMap[animBatchId];
            var uid = batch.CreateAnimation();
            var animation = batch.GetAnimation(uid);
            animation.data.position = position;
            animation.data.rotation = rotation;
            animation.data.scale = scale;
            _uid2BatchId.Add(uid, animBatchId);
            return uid;
        }
        
        public int CreateAnimation(int animBatchId)
        {
            return CreateAnimation(animBatchId, new float3(0, 0, 0), quaternion.identity, new float3(1, 1, 1));
        }
        
        public int CreateAnimation(int animBatchId, float3 position)
        {
            return CreateAnimation(animBatchId, position, quaternion.identity, new float3(1, 1, 1));
        }

        public int CreateAnimation(int animBatchId, float3 position, quaternion rotation)
        {
            return CreateAnimation(animBatchId, position, rotation, new float3(1, 1, 1));
        }

        private GPUBatchAnimation GetBatch(int uid)
        {
            if (!_uid2BatchId.ContainsKey(uid))
            {
                return null;
            }
            var animBatchId = _uid2BatchId[uid];
            return _batchMap[animBatchId];
        }
        
        /// <summary>
        /// 直接获取动画对象
        /// 建议大批量的动画属性设置通过该方法直接获取动画对象后直接进行修改，可以避免调用各种传递uid修改属性导致的内部查表消耗
        /// </summary>
        public GPUAnimation GetAnimation(int uid)
        {
            var batch = GetBatch(uid);
            if (batch != null)
            {
                return batch.GetAnimation(uid);
            }
            return null;
        }
        
        /// <summary>
        /// 设置指定动画的位置
        /// </summary>
        public void SetPosition(int uid, float3 position)
        {
            var batch = GetBatch(uid);
            batch?.SetPosition(uid, position);
        }

        /// <summary>
        /// 设置指定动画的旋转
        /// </summary>
        public void SetRotation(int uid, quaternion rotation)
        {
            var batch = GetBatch(uid);
            batch?.SetRotation(uid, rotation);
        }

        /// <summary>
        /// 设置指定动画的缩放
        /// </summary>
        public void SetScale(int uid, float3 scale)
        {
            var batch = GetBatch(uid);
            batch?.SetScale(uid, scale);
        }
        
        /// <summary>
        /// 播放指定动画
        /// </summary>
        public void PlayAnimation(int uid, string animName, float time = 0)
        {
            var batch = GetBatch(uid);
            batch?.Play(uid, animName, time);
        }

        /// <summary>
        /// 设置当前播放的动画剪辑是否是循环动画
        /// 注：不设置时为默认值，调用 Play 播放时会还原为默认值
        /// </summary>
        public void SetIsLoop(int uid, bool isLoop)
        {
            var batch = GetBatch(uid);
            batch?.SetIsLoop(uid, isLoop);
        }

        /// <summary>
        /// 设置当前动画的播放速率
        /// </summary>
        public void SetTimeScale(int uid, float timeScale)
        {
            var batch = GetBatch(uid);
            batch?.SetTimeScale(uid, timeScale);
        }

        /// <summary>
        /// 暂停当前动画
        /// </summary>
        public void PauseAnimation(int uid)
        {
            var batch = GetBatch(uid);
            batch?.Pause(uid);
        }

        /// <summary>
        /// 恢复当前动画
        /// </summary>
        public void ResumeAnimation(int uid)
        {
            var batch = GetBatch(uid);
            batch?.Resume(uid);
        }

        /// <summary>
        /// 停止当前动画
        /// </summary>
        public void StopAnimation(int uid)
        {
            var batch = GetBatch(uid);
            batch?.Stop(uid);
        }

        /// <summary>
        /// 销毁指定动画对象
        /// </summary>
        public bool DestroyAnimation(int uid)
        {
            var batch = GetBatch(uid);
            if (batch != null)
            {
                return batch.DestroyAnimation(uid);
            }
            return false;
        }

        /// <summary>
        /// 更新动画逻辑
        /// 注意：该方法不包含绘制，绘制统一在 GPUInstancingMgr.Draw 方法中实现
        /// </summary>
        public void Update(float deltaTime)
        {
            foreach (var kv in _batchMap)
            {
                var batch = kv.Value;
                batch.Update(deltaTime);
            }
        }

        /// <summary>
        /// 清除所有动画数据
        /// </summary>
        public void Clear()
        {
            foreach (var kv in _batchMap)
            {
                var batch = kv.Value;
                batch.Dispose();
            }
            _batchMap.Clear();
            _uid2BatchId.Clear();
        }
    }
}
